package com.study.manage.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

public final class DateTimeUtil {

    private DateTimeUtil() {}

    public static final long ONE_DAY_MILSEC = 24 * 60 * 60 * 1000;
    public static final long FIRST_DAY_OFFSET_MILSEC = 8 * 60 * 60 * 1000;
    public static final long ONE_DAY_SEC = 24 * 60 * 60;
    /**
     * 日期格式 yyyy
     */
    public static final String DATE_PATTERN_YEARS = "yyyy";
    /**
     * 日期格式 yyyy
     */
    public static final String DATE_PATTERN_MM = "MM";
    /**
     * 日期格式 yyyy-MM
     */
    public static final String DATE_PATTERN_MONTH = "yyyy-MM";
    /**
     * 年月份
     */
    public static final String DATE_PATTERN_MONTH_SHORT = "yyyyMM";
    /**
     * 日期格式 yyyy-MM-dd
     */
    public static final String DATE_PATTERN_SHORT = "yyyy-MM-dd";
    /**
     * 日期格式 -- yyyyMMdd
     */
    public static final String DATE_COMPRESS_PATTERN = "yyyyMMdd";
    /**
     * 日期格式 yyyy-MM-dd HH:mm:ss
     */
    public static final String DATE_PATTERN_LONG = "yyyy-MM-dd HH:mm:ss";
    /**
     * 日期格式 yyyyMMdd HH:mm:ss
     */
    public static final String DATE_PATTERN = "yyyyMMdd HH:mm:ss";
    /**
     * 日期格式 yyyyMMddHHmmss
     */
    public static final String DATE_PATTERN_LONG_COMPRESS = "yyyyMMddHHmmss";

    public static final int DATE_START_INDEX_OF_DEFAULT_PATTERN = 8;

    private static final String INVALID_PARAM_MSG = "The date could not be null!";
    public static final String TRADE_BEGIN_TIME = "00:00:00";
    public static final String TRADE_END_TIME = "23:59:59";

    /**
     * 计算2个日期相差多少 天 (d1-d2)
     * 
     * @param d1
     * @param d2
     * @return
     */
    public static long dateDayDiff(Date d1, Date d2) {
        long millis1 = d1.getTime() + FIRST_DAY_OFFSET_MILSEC;
        long millis2 = d2.getTime() + FIRST_DAY_OFFSET_MILSEC;
        long day1 = millis1 / ONE_DAY_MILSEC;
        long day2 = millis2 / ONE_DAY_MILSEC;
        return day1 - day2;
    }

    /**
     * 以"天"为单位计算两个日期间的间隔(endDate - beginDate)<br>
     * <br>
     * 例如：<br>
     * 2010-10-10 14:04:00 与 2010-10-10 23:59:59的间隔为 0 <br>
     * 2010-10-10 23:59:59 与 2010-10-11 00:00:00的间隔为 1 <br>
     * 2010-10-11 00:00:00 与 2010-10-10 23:59:59的间隔为 -1 <br>
     * 
     * @param beginDate
     * @param endDate
     * @return 天数, 如果beginDate或者endDate为Null则会抛出运行时异常
     * @author michael.yangf
     */
    public static long countLoanTerm(Date beginDate, Date endDate) {
        return DateTimeUtil.dateDayDiff(endDate, beginDate);
    }

    /**
     * 获取标准的时间,格式为"yyyy-mm-dd"，去除param中的时分秒和毫秒信息<br>
     * 如果输入参数为null,则返回null<br>
     * <br>
     * 输入参数不会被修改,返回的是新的对象
     * 
     * @param param
     * @return
     * @author michael.yangf
     */
    public static Date getStandardDate(Date param) {
        if (null == param) {
            return null;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(param);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar.getTime();
    }

    public static String getFormateDate(Date date, String pattern) {
        return date != null ? new SimpleDateFormat(pattern).format(date) : null;
    }

    /**
     * 返回年月日格式 yyyy-MM-dd
     * 
     * @param date
     * @return 如果参数为null，返回null
     */
    public static String getYMD(Date date) {
        return getYMD(date, DATE_PATTERN_SHORT);
    }
    
    /**
     * 返回年月日格式 yyyy.MM.dd
     * 
     * @param date
     * @return 如果参数为null，返回null
     */
    public static String getYYmmDD(Date date) {
    	String str = getYMD(date, DATE_PATTERN_SHORT);
        return str.replaceAll("-",".");
    }

    /**
     * 返回年月日格式 yyyy-MM
     * 
     * @param date
     * @return 如果参数为null，返回null
     */
    public static String getYM(Date date) {
        return getYMD(date, DATE_PATTERN_MONTH);
    }

    /**
     * 返回年月日格式 yyyyMMDD
     * 
     * @param date
     * @return 如果参数为null，返回null
     */
    public static String getYMD(Date date, String pattern) {
        return date != null ? new SimpleDateFormat(pattern).format(date) : null;
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyy-MM-dd HH:mm:ss"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date parse2Date(String timeDate) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(DATE_PATTERN_LONG);
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyyMMdd HH:mm:ss"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date StringParse2Date(String timeDate) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(DATE_PATTERN);
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyyMMdd HH:mm:ss"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date StringParse3Date(String timeDate) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 解析Date为String<br>
     * 
     * @param date 时间
     * @param format 需转换的格式
     * @return
     */
    public static String parse2String(Date date, String format) {
        if (null == date || StringUtils.isBlank(format)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(format);
        String dateStr = formate.format(date);
        return dateStr;
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyy-MM-dd"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date parse2DateShortPatten(String timeDate) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(DATE_PATTERN_SHORT);
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyyMMdd"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date parse2DateYMDShortPatten(String timeDate) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(DATE_COMPRESS_PATTERN);
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyy-MM"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date parse2DateMonthPatten(String timeDate) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(DATE_PATTERN_MONTH);
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 按秒偏移,根据{@code source}得到{@code seconds}秒之后的日期<Br>
     * 
     * @param source , 要求非空
     * @param seconds , 秒数,可以为负
     * @return 新创建的Date对象
     * @throws 如果source为null , 会抛出IllegalArgumentException异常
     * @author xueye.duanxy
     */
    public static Date addSeconds(Date source, int seconds) {
        return addDate(source, Calendar.SECOND, seconds);
    }

    /**
     * 按分钟偏移,根据{@code source}得到{@code minutes}分钟之后的日期<Br>
     * 
     * @param source , 要求非空
     * @param minutes , 分钟数,可以为负
     * @return 新创建的Date对象
     * @throws 如果source为null , 会抛出IllegalArgumentException异常
     * @author zhipeng.zouzhp
     */
    public static Date addMinutes(Date source, int minutes) {
        return addDate(source, Calendar.MINUTE, minutes);
    }

    /**
     * 按日偏移,计算source指定日期的days天后的日期<Br>
     * 
     * @param source , 要求非空
     * @param days , 天数,可以为负
     * @return 新创建的Date对象
     * @throws 如果source为null , 会抛出IllegalArgumentException异常
     * @author michael.yangf
     */
    public static Date addDays(Date source, int days) {
        return addDate(source, Calendar.DAY_OF_MONTH, days);
    }

    /**
     * addhours
     * 
     * @param date
     * @param hours
     * @return
     */
    public static Date addHours(Date date, int hours) {
        return DateUtils.addHours(date, hours);
    }

    /**
     * 按月偏移,计算source指定日期的months月后的日期<Br>
     * 
     * @param source , 要求非空
     * @param months , 月数,可以为负
     * @return 新创建的Date对象
     * @throws 如果source为null , 会抛出IllegalArgumentException异常
     * @author michael.yangf
     * @since 1.3.0
     */
    public static Date addMonths(Date source, int months) {
        return addDate(source, Calendar.MONTH, months);
    }

    /**
     * 按年偏移,计算source指定日期的years年后的日期<Br>
     * 
     * @param source , 要求非空
     * @param years , 年数,可以为负
     * @return 新创建的Date对象
     * @throws 如果source为null , 会抛出IllegalArgumentException异常
     * @author michael.yangf
     * @since 1.3.0
     */
    public static Date addYears(Date source, int years) {
        return addDate(source, Calendar.YEAR, years);
    }

    /**
     * 计算两个日期的间隔月份
     * 
     * @param beginDate
     * @param endDate
     * @return endDate - beginDate的间隔月份
     */
    public static int calMonthDiff(Date beginDate, Date endDate) {

        Calendar beginCal = Calendar.getInstance();
        beginCal.setTime(beginDate);

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);

        int yearDiff = endCal.get(Calendar.YEAR) - beginCal.get(Calendar.YEAR);
        int monthDiff = 12 * yearDiff + endCal.get(Calendar.MONTH) - beginCal.get(Calendar.MONTH);

        return monthDiff;
    }

    /**
     * 判断当前日期是否为该月的最后一天
     * 
     * @param date
     * @return
     */
    public static boolean isLastDayOfMonth(Date date) {
        Calendar cald = Calendar.getInstance();
        cald.setTime(date);
        int today = cald.get(Calendar.DAY_OF_MONTH);
        int lastDayOfMonth = cald.getActualMaximum(Calendar.DAY_OF_MONTH);
        return today == lastDayOfMonth;

    }

    /**
     * 判断当前日期是否为该月的第一天
     * 
     * @param date
     * @return
     */
    public static boolean isFirstDayOfMonth(Date date) {
        Calendar cald = Calendar.getInstance();
        cald.setTime(date);
        int today = cald.get(Calendar.DAY_OF_MONTH);
        return today == 1;

    }

    /**
     * 只比较时间而忽略日期。 如2010-01-01 12:00:00 与 2010-01-02 11:00:00比较返回true，因为12点比11点晚。
     * 
     * @param curDate
     * @param baseDate
     * @return
     */
    public static boolean laterThanBaseIgnoreDate(Date curDate, Date baseDate) {
        long millis1 = curDate.getTime() + FIRST_DAY_OFFSET_MILSEC;
        long millis2 = baseDate.getTime() + FIRST_DAY_OFFSET_MILSEC;
        long curTime = millis1 % ONE_DAY_MILSEC;
        long baseTime = millis2 % ONE_DAY_MILSEC;

        return curTime > baseTime;
    }

    /**
     * 根据参数指定的年月日信息生成Date对象，其时分秒、毫秒均为0<br>
     * 注意，和Calendar类型的月份数不一样，本方法的month参数的有效范围是1-12<br>
     * 
     * @param year
     * @param month
     * @param date
     * @return 如果month参数不合法，返回null
     */
    public static Date getDate(int year, int month, int date) {

        if (month < 1 || month > 12) {
            return null;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.set(year, month - 1, date, 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar.getTime();
    }

    /**
     * 获取指定日期的DAY_OF_MONTH信息<br>
     * 
     * @param date
     * @return 日期的DAY_OF_MONTH信息
     * @throws 如果source为null , 会抛出IllegalArgumentException异常
     * @author michael.yangf
     * @since 2.0.0
     */
    public static short getDayOfMonth(Date date) {
        if (date == null) {
            throw new IllegalArgumentException(INVALID_PARAM_MSG);
        }

        Calendar c = Calendar.getInstance();
        c.setTime(date);

        return (short) c.get(Calendar.DAY_OF_MONTH);
    }

    // ---------------------------------------- Private 函数
    // ------------------------------------------

    // Add Date并且返回一个新的日历对象
    private static Date addDate(Date date, int calendarField, int amount) {
        if (date == null) {
            throw new IllegalArgumentException(INVALID_PARAM_MSG);
        }

        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(calendarField, amount);
        return c.getTime();
    }

    /**
     * 获取指定日期的当月天数
     * 
     * @param date
     * @return
     */
    public static int getDaysInMonth(Date date) {

        if (date == null) {
            throw new IllegalArgumentException(INVALID_PARAM_MSG);
        }

        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    public static String getDaysInWeek(Date date) {
        String[] weekDays = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
        if (date == null) {
            throw new IllegalArgumentException(INVALID_PARAM_MSG);
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int w = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0)
            w = 0;
        return weekDays[w];
    }

    /**
     * 获取指定日期中日的字符串 如2010-01-01 得到 01
     * 
     * @param date
     * @return
     */
    public static String getDatesStrInMonth(Date date) {
        if (date == null) {
            throw new IllegalArgumentException(INVALID_PARAM_MSG);
        }
        String dateStr = DateTimeUtil.getYMD(date);
        return StringUtils.substring(dateStr, DATE_START_INDEX_OF_DEFAULT_PATTERN);

    }

    /**
     * 判断date是否在左开右闭区间(start, end]中
     * 
     * @param date 校验日期
     * @param start 区间起始日
     * @param end 区间截止日
     * @return 在区间(start, end]中则返回true，否则返回false
     */
    public static boolean isBetween(Date date, Date start, Date end) {
        return countLoanTerm(start, date) > 0 && countLoanTerm(date, end) >= 0;
    }

    /**
     * 获取最近的一次时间，空时间不进行比较。全部为空返回null
     * 
     * @return
     */
    public static Date getLastDate(Date... dates) {
        List<Date> realDateList = new ArrayList<Date>();
        for (Date date : dates) {
            if (null != date) {
                realDateList.add(date);
            }
        }

        Date maxDate = null;
        if (realDateList.size() != 0) {
            maxDate = realDateList.get(0);
            for (Date realDate : realDateList) {
                if (realDate.after(maxDate)) {
                    maxDate = realDate;
                }
            }
        }
        return maxDate;
    }

    /**
     * @param date1
     * @param date2
     */
    public static Date getMin(Date date1, Date date2) {
        return countLoanTerm(date1, date2) >= 0 ? date1 : date2;
    }

    /**
     * @param date1
     * @param date2
     */
    public static Date getMax(Date date1, Date date2) {
        return countLoanTerm(date1, date2) >= 0 ? date2 : date1;
    }

    /**
     * 返回只有年月日的昨天
     * 
     * @return
     */
    public static Date getYesterday() {
        Date yesterday = DateTimeUtil.addDays(new Date(), -1);
        return DateTimeUtil.getStandardDate(yesterday);
    }

    public static Date parseToDesignatedDate(Date date, String time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd " + time);
        Date end = null;
        try {
            end = sdf.parse(sdf1.format(date));
        } catch (ParseException e) {

        }
        return end;
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyyMM"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date StringParseDate(String timeDate) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(DATE_PATTERN_MONTH_SHORT);
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

    public static Date lastDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DATE));
        return cal.getTime();
    }

    public static Date firstDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getMinimum(Calendar.DATE));
        cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR));
        cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE));
        cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND));
        return cal.getTime();
    }

    public static String format(Date date, String pattern) {
        return new SimpleDateFormat(pattern).format(date);
    }

    /**
     * 
     * <p>
     * Title: addDateByMonth
     * </p>
     * <p>
     * Description:
     * </p>
     * 
     * @param date
     * @param month
     * @return
     */
    public static Date addDateByMonth(Date date, int month) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MONTH, month);
        return cal.getTime();
    }

    /**
     * 解析String为Date<br>
     * String的格式要求为"yyyy-MM-dd"<br>
     * 
     * @param timeDate 如果为空字符串含null，返回处理结果为null
     * @param pattern String的格式要求（例"yyyyMMddHHmm"）
     *
     * @return 解析完毕的Date数据，如果发生解析异常则返回null
     */
    public static Date parseDate2Patten(String timeDate, String pattern) {
        if (StringUtils.isBlank(timeDate)) {
            return null;
        }
        SimpleDateFormat formate = new SimpleDateFormat(pattern);
        Date date;
        try {
            date = formate.parse(timeDate);
            return date;
        } catch (ParseException e) {
            return null;
        }
    }

}
